package com.gitee.feizns.webtemplate.commons.wechat.impl;

import com.gitee.feizns.webtemplate.commons.utils.encryption.EncryptUtils;
import com.gitee.feizns.webtemplate.commons.wechat.WeChatException;
import com.gitee.feizns.webtemplate.commons.wechat.WeChatProperties;
import com.gitee.feizns.webtemplate.commons.wechat.WeChatService;
import com.gitee.feizns.webtemplate.commons.wechat.WeChatUserInfo;
import com.gitee.feizns.webtemplate.commons.wechat.entity.AuthorizeToken;
import com.gitee.feizns.webtemplate.commons.wechat.entity.AuthorizeUser;
import com.gitee.feizns.webtemplate.commons.wechat.entity.FaJsApiTicket;
import com.gitee.feizns.webtemplate.commons.wechat.entity.FvJsApiTicket;
import com.gitee.feizns.webtemplate.commons.wechat.entity.Message;
import com.gitee.feizns.webtemplate.commons.wechat.entity.Token;
import org.springframework.web.client.RestTemplate;

import java.text.MessageFormat;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author feizns
 * @since 2019/4/12
 */
public class WeChatServiceImpl implements WeChatService {

    /**
     * access_token
     */
    private Token token;

    /**
     * token 的有效截至时间
     */
    private long tokenEndTime;

    /**
     * 微信js api ticket
     */
    private FaJsApiTicket jsApiTicket;

    /**
     * ticket 的有效截至时间
     */
    private long ticketEndTime;

    /**
     * 微信相关配置
     */
    private WeChatProperties properties;

    public WeChatServiceImpl(WeChatProperties properties, RestTemplate restTemplate) {
        this.properties = properties;
        this.restTemplate = restTemplate;
    }

    /**
     *
     */
    private RestTemplate restTemplate;

    /**
     * 获取 access_token
     * @return
     */
    @Override
    public String getAccessToken() {
        return getToken().getAccess_token();
    }

    /**
     * 获取js api 签名权限
     * @param url
     * @return
     */
    @Override
    public FvJsApiTicket getJsApiTicket(String url) {
        return createFvJsApiTicket(url);
    }

    /**
     * 获取公众号 appId
     * @return
     */
    @Override
    public String getAppId() {
        return properties.getAppId();
    }

    @Override
    public AuthorizeToken getAuthorizeToken(String code) {
        return restTemplate.getForObject(properties.getAuthorizeUrl(), AuthorizeToken.class,
                properties.getAppId(),
                properties.getAppSecret(),
                code);
    }

    @Override
    public AuthorizeUser getAuthorizeUserInfo(String code) {
        AuthorizeToken token = getAuthorizeToken(code);
        return restTemplate.getForObject(properties.getAuthorizeUserInfoUrl(), AuthorizeUser.class,
                token.getAccess_token(),
                token.getOpenId());
    }

    @Override
    public WeChatUserInfo getUserInfo(String openId) {
        Token token = getToken();
        return restTemplate.getForObject(properties.getUserInfoUrl(), WeChatUserInfo.class,
                token.getAccess_token(), openId);
    }

    @Override
    public WeChatUserInfo getUserInfoByCode(String code) {
        AuthorizeToken token = getAuthorizeToken(code);
        return getUserInfo(token.getOpenId());
    }

    private FvJsApiTicket createFvJsApiTicket(String url) {
        FvJsApiTicket ticket = new FvJsApiTicket();
        ticket.setAppId( properties.getAppId() );
        ticket.setNonceStr( UUID.randomUUID().toString() );
        ticket.setTimestamp( System.currentTimeMillis() / 1000 );
        ticket.setSignature( generatorSignature(ticket.getTimestamp().toString(), ticket.getNonceStr(), url) );
        return ticket;
    }

    /**
     * 生成签名
     * @param timestamp
     * @param nonceStr
     * @param url
     * @return
     */
    private String generatorSignature(String timestamp, String nonceStr, String url) {
        String result = MessageFormat.format(
                properties.getJsApiSignatureUrl(),
                getJsApiTicket().getTicket(),
                nonceStr,
                timestamp,
                url);
        return EncryptUtils.sha_1(result);
    }

    /**
     * 获取token 没有或过期就请求微信
     * @return
     */
    public Token getToken() {
        if ( token == null || isInvalid(tokenEndTime) )
            token = restTemplate.getForObject(properties.getAccessTokenUrl(), Token.class, properties.getAppId(), properties.getAppSecret());

        checkMessage(token);

        return token;
    }

    public FaJsApiTicket getJsApiTicket() {
        if ( jsApiTicket == null || isInvalid(tokenEndTime) )
            jsApiTicket = restTemplate.getForObject(properties.getJsApiTicketUrl(), FaJsApiTicket.class, getAccessToken());

        checkMessage(jsApiTicket);

        return jsApiTicket;
    }

    /**
     * 10分钟
     */
    private static final long TEN_MINUTES = (TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES));

    /**
     * 验证是否有效，提前10分钟
     * @param endTime
     * @return
     */
    private static final boolean isInvalid(long endTime) {
        return System.currentTimeMillis() >= endTime - TEN_MINUTES;
    }

    private void checkMessage(Message message) {
        if ( message.isSuccess() == false )
            throw new WeChatException(message.getErrmsg());
    }

}
